---
title: প্রভাইডার রিড করা
---

import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
import CodeBlock from "@theme/CodeBlock";
import counter from "!!raw-loader!./reading_counter.dart";
import consumerWidget from "!!raw-loader!./reading_consumer_widget.dart";
import consumerStatefulWidget from "!!raw-loader!./reading_consumer_stateful_widget.dart";
import consumerHookWidget from "!!raw-loader!./reading_consumer_hook_widget.dart";
import consumerHook from "!!raw-loader!./reading_consumer_hook.dart";
import watch from "!!raw-loader!./reading_watch.dart";
import watchBuild from "!!raw-loader!./reading_watch_build.dart";
import listen from "!!raw-loader!./reading_listen.dart";
import listenBuild from "!!raw-loader!./reading_listen_build.dart";
import read from "!!raw-loader!./reading_read.dart";
import readBuild from "!!raw-loader!./reading_read_build.dart";
import readNotifierBuild from "!!raw-loader!./reading_read_notifier_build.dart";
import watchNotifierBuild from "!!raw-loader!./reading_watch_notifier_build.dart";
import { trimSnippet } from "../../../../../src/components/CodeSnippet";

এই গাইডটি পড়ার আগে, খেয়াল রাখবেন যেন প্রথমে [যেন আপনি প্রভাইডার এর ব্যাপারে পড়েন](../concepts2/providers)।

আমরা এই গাইড এ দেখব কিভাবে একটি প্রভাইডার কনসিউম করতে পারব

## একটি "ref" অবজেক্ট ধারণ করা

প্রথমে এবং শুরুতে কোন প্রভাইডার পড়ার আগে আমাদের "ref" অবজেক্টটি ধারণ করতে হবে।

এই অবজেক্টটি আমাদের অনুমতি দেই প্রভাইডারদের সাথে আদানপ্রদান করার, সেটি উইজেট ও করতে পারেন অথবা
অন্য প্রভাইডার থেকে ও পারবেন।

### প্রভাইডার হতে একটি "ref" অবজেক্ট ধারণ করা

সকল প্রভাইডার "ref" প্যারামিটার হিসেবে পাইঃ

```dart
final provider = Provider((ref) {
  // ref দিয়ে অন্য প্রভাইডার ধারণ করুন
  final repository = ref.watch(repositoryProvider);

  return SomeValue(repository);
})
```

এই প্যারামিটার প্রভাইডার দ্বারা উন্মুক্ত ভ্যালু পাস করা নিরাপদ.

উদাহারণসরূপ, একটি কমন সিনারিও হচ্ছে, "ref" অবজেক্টটি [StateNotifier] এ পাস করাঃ

<CodeBlock>{trimSnippet(counter)}</CodeBlock>

এরকম করার পরে আমদের `Counter` ক্লাস অন্য প্রভাইডার পরার অনুমতি পায়।

### উইজেট হতে একটি "ref" অবজেক্ট ধারণ করা

উইজেটে প্রাকৃতিক ভাবে কোন "ref" অবজেক্ট থাকে না. কিন্তু [Riverpod] একাধিক সমাধান অফার করে এই অবজেক্টটি
ধারণ করার জন্যে।

#### ConsumerWidget এ এক্সটেন্ড করা StatelessWidget এর পরিবর্তে

সবচেয়ে কমন সমাধান হচ্ছে আমাদের [StatelessWidget] কে [ConsumerWidget] দিয়ে পরিবর্তন করা,
যখন আমরা একটি উইজেট তৈরি করি।

[ConsumerWidget] অনেকাংশে [StatelessWidget], শুধুমাত্র কিছু পরিবর্তন যেমন এর
বিল্ড মেথড একটি এক্সট্রা প্যারমিটার পায় যা হচ্ছেঃ "ref" অবজেক্ট

একটি সাধারণ [ConsumerWidget] দেখতে এরকম হবেঃ

<CodeBlock>{trimSnippet(consumerWidget)}</CodeBlock>


#### ConsumerStatefulWidget+ConsumerState এ এক্সটেন্ড করা StatefulWidget+State এর পরিবর্তে

[ConsumerWidget] এর মতই, [ConsumerStatefulWidget] এবং [ConsumerState] এর সমতুল্য হচ্ছে
[StatefulWidget] এবং এর [State], আর তফাৎ হচ্ছে এটিতে "ref" অবজেক্ট রয়েছে

এবার, "ref" বিল্ড মেথড এ একটি প্যারামিটার হিসেবে পাস করা হয় না, বরং এটি [ConsumerState] অবজেক্ট এর
একটি প্রপার্টিঃ

<CodeBlock>{trimSnippet(consumerStatefulWidget)}</CodeBlock>

#### HookConsumerWidget এক্সটেন্ড করা HookWidget এর পরিবর্তে

এই সলিউশন টা [flutter_hooks] ব্যবহারকারীদের জন্যে. কারণ [flutter_hooks] চায়
 [HookWidget] এক্সটেন্ড করা এটি কাজ করার জন্য, যে উইজেট গুলা hooks ব্যবহার করে সেগুলা 
[ConsumerWidget] এক্সটেন্ড করতে পারে না.

একটি সমাধান, যেটি [hooks_riverpod] প্যাকেজ দ্বারা সম্ভব হয়েছে, তা হচ্ছে [HookWidget] কে [HookConsumerWidget] রিপ্লেস করা.
[HookConsumerWidget] একি সাথে [ConsumerWidget] এবং [HookWidget] এর কাজ একসাথে করে.এটি আমাদের অনুমতি 
দেই একইসাথে প্রভাইডার লিসেন/রিড করার এবং hooks ব্যবহার করার।

একটি উদাহারণ হবে:

<CodeBlock>{trimSnippet(consumerHookWidget)}</CodeBlock>

#### Consumer and HookConsumer widgets

একটি ফাইনাল সমাধান হবে "ref" অবজেক্টকে উইজেট এর মধ্যে ধারণ করার তা হল
[Consumer]/[HookConsumer] এর উপর নির্ভর করা.


এই ক্লাসগুলি হল উইজেট যা একই সাথে "ref" পেতে ব্যবহার করা যেতে পারে যা
[ConsumerWidget]/[HookConsumerWidget] এর একই বৈশিষ্ট্য।

এই উইজেটগুলো একটি রাস্তা হতে পারে একটি "ref" অবজেক্ট পাওয়ার কোন ক্লাস ডিফাইন করা ছাড়া। 
উদাহরণসরূপঃ

<CodeBlock>{trimSnippet(consumerHook)}</CodeBlock>

## Ref দিয়ে প্রভাইডার এর সাথে আদানপ্রদান

এখন আমরা যখন "ref" পেয়ে গেছি, আমরা এটা ব্যবহার শুরু করতে পারি।

"ref" এর তিনটি ব্যবহার বিধি রয়েছেঃ

- একটি প্রভাইডার এর ভ্যালু ধারণ এবং এটির বদলানো লিসেন করা, যাতে যখন ভ্যালু বদলায়, তখন এই উইজেটটি
  বা প্রভাইডার যেটি এই ভ্যালুতে সাবস্ক্রাইবড আছে ওটি রিবিল্ড হবে।
  এটি করা যাবে `ref.watch` দিয়ে.
- একটি প্রভাইডার এ লিসেনার এড করার মাধ্যমে, যাতে যেখনো একটি একশ্যান নেওয়া যায় যখন 
  প্রভাইডারটি বদলায়।
  এটি করা যাবে `ref.listen` দিয়ে.
- একটি প্রভাইডার এর শুধু ভ্যালু ধারণ করা একইসাথে বদলানোকে এড়িয়ে যাওয়া
  এটি খুবি উপকারী যখন আমাদের প্রভাইডার এর শুধু একটি ভ্যালু প্রয়োজন হয়, কোন ইভেন্ট কলব্যাক
  ফাংশন এ, যেমন একটি `onPressed` কলব্যাক ফাংশন।
  এটি করা যাবে `ref.read` দিয়ে.

:::note
যখনি সম্ভব, চাইবেন `ref.watch` ব্যবহার করতে `ref.read` অথবা `ref.listen` এর পরিবর্তে
একটি ফিচার ইমপ্লিমেন্ট করার জন্যে। আপনার ইমপ্লিমেন্টেশন `ref.watch` এ পরিবর্তন করার পর, এটি একইসাথে রিএক্টিভ এবং ডিক্লেরাটিভ হয়ে যায়, যেটি আপনার এপ্লিক্যাশন আরো মেইন্টেইনেভেল বানাই।
:::

### ref.watch দিয়ে একটি প্রভাইডার অবসার্ব করা

`ref.watch` কে একটি উইজেট এর `build` মেথড এর ভিতর অথবা
একটি প্রভাইডার এর বডির ভিতরে ব্যবহার করা যায়, যাতে আমরা ওই উইজেট/প্রভাইডার থেকে 
`ref.watch` কল ব্যবহার করে এই কলের প্রভাইডার অবসার্ব করতে পারি:

উদাহরণসরূপ, একটি প্রভাইডার `ref.watch` ব্যবহার করে একাধিক প্রভাইডার একত্রে করে একটি নতুন
ভ্যালুতে রূপান্তর করতে পারে।

একটি উদাহারণ হবে একটি টুডু-লিস্ট ফিলটার করা।

আমাদের দুইটি প্রভাইডার থাকতে পারে:

- `filterTypeProvider`, একটি প্রভাইডার যা বর্তমানের ফিলটার টাইপ এক্সপোস করে। যেমনঃ
  (none, show only completed tasks, ...)
- `todosProvider`, একটি প্রভাইডার যা সকল টুডুলিস্ট এক্সপোস করে

এবং `ref.watch` ব্যবহার করে,আমরা একটি তৃতীয় প্রভাইডার বানাতে পারি উপরোক্ত দুইটি প্রভাইডার ব্যবহার করে এবং এটি আমাদের একটি ফিলটার করা টুডু লিস্ট দিবেঃ

<CodeBlock>{trimSnippet(watch)}</CodeBlock>

এই কোড দ্বারা, `filteredTodoListProvider` এখন ফিল্টার করা টুডু এক্সপোস করে.

ফিল্টার করা লিস্টও স্বয়ংক্রিয়ভাবে আপডেট হবে যদি ফিল্টার বা টুডুর তালিকা
পরিবর্তিত হয়। তবুও একই সময়ে, ফিল্টার করা তালিকাটি পুনরায় গণনা করা হবে না যদি
ফিল্টার বা কাজের তালিকা কোনটিই পরিবর্তন না হয়।

একই ভাবে, একটি উইজেট `ref.watch` ব্যবহার করে একটি ইউজার ইন্টারফেস দেখাতে পারে যেটি 
প্রভাইডার এর উপর ডিপেন্ডেডঃ

<CodeBlock>{trimSnippet(watchBuild)}</CodeBlock>

এই কোড স্নিপেট হতে আমরা দেখতে পাচ্ছি, এখানে একটি উইজেট একটি প্রভাইডার অবসার্ব করতেছে 
যেটি একটি কাউন্টার স্টোর করে, এবং যদি কাউন্টারটি পরিবর্তিত হয়, তাহলে এই উইজেটটি রিবিল্ড হবে এবং ইউয়াই
ও পরিবর্তন হবে নতুন ভ্যালু এর সাথে।

:::caution
মনে রাখতে হবেঃ `watch` মেথড এসোঙ্ক্রোনাসলি (asynchronously) কল করা যাবে না।
যেমন একটি [ElevatedButton] এর `onPressed` কলব্যাকে. এবং `initState` এর ভিতর 
আর [State] এর লাইফসাইকেলে ও ব্যবহার করা যাবে না।

ওসবক্ষেত্রে, `ref.read` ব্যবহার করতে পারেন।
:::

### ref.listen ব্যবহার করে প্রভাইডার এর পরিবর্তনে রিএক্ট করা

একইভাবে `ref.watch` এর মত, `ref.listen` ব্যবহার করেও আমরা একটি প্রভাইডার অবসার্ব করতে পারব।  

তাদের মধ্যে মূল পার্থক্য হচ্ছে, যে প্রভাইডারটি লিসেন করা হচ্ছে সেটার ভ্যালু পরবির্তন হলে এটি উইজেট/প্রভাইডার রিবিল্ড না করে, `ref.listen` একটি কাস্টম ফাংশন কল করে।

এটি অনেকক্ষেত্রে ব্যবহারযোগ্য হতে পারে যখন কোন পরিবর্তন হয়, যেমন একটি স্ন্যাকবার দেখানো যখন কোন
ত্রুটি বা এরর আসে বা হয়। 

`ref.listen` মেথড এর জন্য 2টি অবস্থানগত আর্গুমেন্টের প্রয়োজন, প্রথমটি হল প্রভাইডার এবং দ্বিতীয়টি হল কলব্যাক ফাংশন যা আমরা স্টেট পরিবর্তনের সময় কার্যকর করতে চাই৷

কলব্যাক ফাংশন যখন কল করা হয় তখন 2টি ভ্যালু পাওয়া যাবে, পূর্ববর্তী স্টেটের ভ্যালু এবং নতুন স্টেটের ভ্যালু।

`ref.listen` মেথডটি একটি প্রভাইডার এর বডিতে ও ব্যবহার করা যাবেঃ

<CodeBlock>{trimSnippet(listen)}</CodeBlock>

অথবা একটি উইজেট এর `build` মেথড এ:

<CodeBlock>{trimSnippet(listenBuild)}</CodeBlock>

:::caution
`listen` মেথড এসোঙ্ক্রোনাসলি (asynchronously) কল করা যাবে না,
যেমন একটি [ElevatedButton] এর `onPressed` কলব্যাকে. এবং `initState` এর ভিতর 
আর [State] এর লাইফসাইকেলে ও ব্যবহার করা যাবে না।
:::

### Using ref.read to obtain the state of a provider once

`ref.read` মেথড হল কোনো একটি প্রভাইডারের অবস্থা ধারণ করা, অতিরিক্ত কোনো কিছু ছাড়াই।

এটি সাধারণত ব্যবহার করা হয় সেই ফাংশনগুলোতে যেগুলা ইউজাররা ইন্টারেকশেন দ্বারা ট্রিগ্রার হয়।
উদাহরণস্বরূপ, যখন একজন ব্যবহারকারী একটি বাটনে ক্লিক করেন তখন আমরা একটি কাউন্টার বৃদ্ধি করতে `ref.read` ব্যবহার করতে পারিঃ

<CodeBlock>{trimSnippet(read)}</CodeBlock>

:::note
`ref.read` যত পারা যায় তত কম ইউজ করা ভাল.

এটি বিদ্যমান কারণ অনেক সময় `watch` অথবা `listen` ব্যবহার করা অনেক বেশি অসুবিধাজনক হবে, সেইসব জায়গায়
এটি ব্যবহার করুন।

যদি আপনি পারেন, এটি প্রায় সবসময় ভাল হবে `watch`/`listen` ব্যবহার করা, বিশেষ করে `watch`.
:::

#### **কখনো** `ref.read` বিল্ড মেথড এ ব্যবহার করবেন না

আপনি একটি উইজেটের কর্মক্ষমতা অপ্টিমাইজ করতে `ref.read` ব্যবহার করতে প্রলুব্ধ হতে পারেন
নিচের মতঃ

<CodeBlock>{trimSnippet(readBuild)}</CodeBlock>

কিন্তু এটি একটি খুব খারাপ অভ্যাস এবং এর কারণে এমন বাগ হতে পারে যা ট্র্যাক করা কঠিন।

এইভাবে `ref.read` ব্যবহার করা সাধারণত এই চিন্তার সাথে যুক্ত হয় "কোনও প্রভাইডার দ্বারা প্রকাশিত ভ্যালু কখনই পরিবর্তিত হয় না তাই `ref.read` ব্যবহার করা নিরাপদ"। এই অনুমানের সাথে সমস্যাটি হল যে, যদিও আজ সেই প্রভাইডার প্রকৃতপক্ষে তার মান আপডেট করতে পারে না, কোন গ্যারান্টি নেই যে আগামীকাল একই হবে।

সফ্টওয়্যার অনেক পরিবর্তন হতে থাকে, এবং সম্ভবত ভবিষ্যতে, এমন একটি ভ্যালু যা আগে কখনও পরিবর্তিত হয়নি তা পরিবর্তন করতে হবে। কিন্তু যদি আপনি `ref.read` ব্যবহার করেন, যখন সেই ভ্যালু পরিবর্তন হতে শুরু করে, তখন
আপনার সম্পূর্ণ কোডবেসের মধ্যে `ref.read` কে `ref.watch`-এ পরিবর্তন করতে হবে - যা ত্রুটি প্রবণ এবং কিছু ক্ষেত্রে আপনি ভুলে যাওয়ার সম্ভাবনা রয়েছে।

আপনি যদি শুরুতে `ref.watch` ব্যবহার করেন তবে আপনার কোনো সমস্যা হবে না।

**_কিন্তু আমি আমার উইজেট রিবিল্ড সংখ্যা কমাতে `ref.read` ব্যবহার করতে চেয়েছিলাম_**

লক্ষ্যটি প্রশংসনীয় হলেও, এটা মনে রাখা গুরুত্বপূর্ণ যে আপনি এর পরিবর্তে `ref.watch` ব্যবহার করে ঠিক একই প্রভাব (বিল্ডের সংখ্যা হ্রাস) অর্জন করতে পারেন।

প্রভাইডাররা পুনর্নির্মাণের সংখ্যা হ্রাস করার সময় একটি ভ্যালু প্রাপ্ত করার বিভিন্ন উপায় অফার করে, যা আপনি এর পরিবর্তে ব্যবহার করতে পারেন।

উদাহরণস্বরূপ এর পরিবর্তে

<CodeBlock>{trimSnippet(readNotifierBuild)}</CodeBlock>

আমরা করতে পারি:

<CodeBlock>{trimSnippet(watchNotifierBuild)}</CodeBlock>

উভয় স্নিপেট একই প্রভাব অর্জন করে: কাউন্টার বৃদ্ধি পেলে আমাদের বাটনটি পুনর্নির্মাণ করবে না।

অন্যদিকে, দ্বিতীয় পদ্ধতিটি এমন ক্ষেত্রে সমর্থন করে যেখানে কাউন্টারটি পুনরায় সেট করা হয়েছে। উদাহরণস্বরূপ, অ্যাপ্লিকেশনটির অন্য একটি অংশ কল করতে পারেঃ

```dart
ref.refresh(counterProvider);
```

যেটি আবার `StateController` অবজেক্টটি তৈরি করবে.

If we used `ref.read` here, our button would still use the previous
`StateController` instance, which was disposed and should no longer be used.
Whereas using `ref.watch` correctly rebuilt the button to use the new `StateController`.

যদি আমরা এখানে `ref.read` ব্যবহার করি, আমাদের বাটনটি এখনও পূর্ববর্তী `StateController` এর ইন্সট্যান্স ব্যবহার করবে, যা নিষ্পত্তি করা হয়েছে এবং আর ব্যবহার করা উচিত নয়। যেখানে `ref.watch` ব্যবহার করে নতুন `StateController` ব্যবহার করার জন্য বাটনটি সঠিকভাবে পুনর্নির্মাণ করা হয়েছে।

## কি Read করতে হবে সিদ্ধান্ত নেয়া

তা আপনি যে প্রভাইডার রিড করতে চান তার উপর নির্ভর করে, আপনার একাধিক সম্ভাব্য ভ্যালু থাকতে পারে 
যা আপনি রিড করতে পারেন।

একটি উদাহরণ হিসাবে, নিম্নলিখিত [StreamProvider] বিবেচনা করুনঃ

```dart
final userProvider = StreamProvider<User>(...);
```

যখন আপনি `userProvider` রিড করবেন, আপনিঃ

- সিঙ্ক্রোনাসলি বর্তমান স্টেট রিড করতে পারবেন `userProvider` লিসেন করে:

  ```dart
  Widget build(BuildContext context, WidgetRef ref) {
    AsyncValue<User> user = ref.watch(userProvider);

    return user.when(
      loading: () => const CircularProgressIndicator(),
      error: (error, stack) => const Text('Oops'),
      data: (user) => Text(user.name),
    );
  }
  ```

- সংযোক্ত [Stream] টি ধারণ করে, `userProvider.stream` লিসেন করে:

  ```dart
  Widget build(BuildContext context, WidgetRef ref) {
    Stream<User> user = ref.watch(userProvider.stream);
  }
  ```

- একটি [Future] ধারণ করে যা নতুন নির্গত ভ্যালু দ্বারা রিসল্ভ করে,`userProvider.future` লিসেন করেঃ

  ```dart
  Widget build(BuildContext context, WidgetRef ref) {
    Future<User> user = ref.watch(userProvider.future);
  }
  ```

অন্যান্য প্রভাইডার বিভিন্ন বিকল্প ভ্যালু অফার করতে পারে.
আরও তথ্যের জন্য, প্রতিটি প্রভাইডার এর [API reference পড়ুন](https://pub.dev/documentation/riverpod/latest/riverpod/riverpod-library.html).

## "select" দিয়ে রিবিল্ড ফিলটার করা 


উল্লেখ করার মত, রিডিং প্রভাইডার এর সাথে সম্পর্কিত একটি চূড়ান্ত ফিচার হল, একটি উইজেট/প্রোভাইডার রিবিল্ড সংখ্যা বা কতবার `ref.listen` একটি ফাংশন নির্বাহ করে তা হ্রাস করার ক্ষমতা।

This is important to keep in mind as, by default, listening to a provider
listens to the entire object. But in some cases, a widget/provider may only
care about some properties instead of the whole object.

ডিফল্টরূপে, প্রভাইডার রিড করার জন্য এটি মনে রাখা গুরুত্বপূর্ণ যে 
পুরো প্রভাইডার রিড করে। কিন্তু কিছু ক্ষেত্রে, একটি উইজেট/প্রোভাইডার পুরো বস্তুর পরিবর্তে শুধুমাত্র কিছু বৈশিষ্ট্যে গুরুত্ব দেই।

উদাহরণস্বরূপ, একজন প্রভাইডার একটি `User` এক্সপোস করতে পারেঃ

```dart
abstract class User {
  String get name;
  int get age;
}
```

কিন্তু একটি উইজেট শুধুমাত্র `User` নাম ব্যবহার করতে পারেঃ

```dart
Widget build(BuildContext context, WidgetRef ref) {
  User user = ref.watch(userProvider);
  return Text(user.name);
}
```

আমরা যদি সহজভাবে `ref.watch` ব্যবহার করি, তাহলে `User` এর `age` পরিবর্তিত হলে
এটি উইজেটটিকে পুনর্নির্মাণ করবে।

সমাধান হল রিভারপডকে স্পষ্টভাবে বলার জন্য `select` ব্যবহার করা যে আমরা 
শুধুমাত্র `User` এর কিছু বৈশিষ্ট্য শুনতে চাই।

আপডেট করা কোড হবেঃ

```dart
Widget build(BuildContext context, WidgetRef ref) {
  String name = ref.watch(userProvider.select((user) => user.name));
  return Text(name);
}
```

এটি যেভাবে কাজ করে তা হল, `select` ব্যবহার করে, আমরা এমন একটি ফাংশন নির্দিষ্ট করছি 
যা আমাদের নির্ধারণ করে প্রাপার্টি প্রদান করে।


তারপর, যখনই `User` পরিবর্তন হবে, রিভারপড এই ফাংশনটিকে কল করবে এবং আগের এবং নতুন ফলাফলের তুলনা করবে। যদি তারা আলাদা হয় (যেমন নাম পরিবর্তন হলে), রিভারপড উইজেটটি পুনর্নির্মাণ করবে।
কিন্তু যদি তারা সমান হয় (যেমন যখন শুধুমাত্র বয়স পরিবর্তিত হয়), তাহলে রিভারপড উইজেটটি পুনর্নির্মাণ করবে না।

:::info
`ref.listen` এর সাথেও `select` ব্যবহার করা সম্ভবঃ

```dart
ref.listen<String>(
  userProvider.select((user) => user.name),
  (String? previousName, String newName) {
    print('The user name changed $newName');
  }
);
```

এমন করলে নাম পরিবর্তন হলেই শুধু লিসেনার কল হবে।
:::

:::tip

আপনাকে অবজেক্ট এর প্রপার্টি ফেরত দিতে হবে না। যে কোনো ভ্যালু এর 
overrides == কাজ করবে। উদাহরণস্বরূপ, আপনি করতে পারেন:

```dart
final label = ref.watch(userProvider.select((user) => 'Mr ${user.name}'));
```

:::

[stateprovider]: https://pub.dev/documentation/hooks_riverpod/latest/legacy/StateProvider-class.html
[providercontainer]: https://pub.dev/documentation/riverpod/latest/riverpod/ProviderContainer-class.html
[providerlistener]: https://pub.dev/documentation/flutter_riverpod/latest/flutter_riverpod/ProviderListener-class.html
[providerscope]: https://pub.dev/documentation/flutter_riverpod/latest/flutter_riverpod/ProviderScope-class.html
[consumer]: https://pub.dev/documentation/flutter_riverpod/latest/flutter_riverpod/Consumer-class.html
[consumerwidget]: https://pub.dev/documentation/flutter_riverpod/latest/flutter_riverpod/ConsumerWidget-class.html
[consumerstate]: https://pub.dev/documentation/flutter_riverpod/latest/flutter_riverpod/ConsumerStatefulWidget-class.html
[consumerstatefulwidget]: https://pub.dev/documentation/flutter_riverpod/latest/flutter_riverpod/ConsumerState-class.html
[useprovider]: https://pub.dev/documentation/hooks_riverpod/latest/hooks_riverpod/ref.watch(.html
[elevatedbutton]: https://api.flutter.dev/flutter/material/ElevatedButton-class.html
[streambuilder]: https://api.flutter.dev/flutter/widgets/StreamBuilder-class.html
[riverpod]: https://github.com/rrousselgit/riverpod
[text]: https://api.flutter.dev/flutter/widgets/Text-class.html
[hooks_riverpod]: https://pub.dev/packages/hooks_riverpod
[future]: https://api.dart.dev/stable/2.13.4/dart-async/Future-class.html
[stream]: https://api.dart.dev/stable/2.13.4/dart-async/Stream-class.html
[hookwidget]: https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/HookWidget-class.html
[hookconsumerwidget]: https://pub.dev/documentation/hooks_riverpod/latest/hooks_riverpod/HookConsumerWidget-class.html
[hookconsumer]: https://pub.dev/documentation/hooks_riverpod/latest/hooks_riverpod/HookConsumer-class.html
[flutter_riverpod]: https://pub.dev/packages/flutter_riverpod
[flutter_hooks]: https://github.com/rrousselGit/flutter_hooks
[statenotifier]: https://pub.dev/documentation/state_notifier/latest/state_notifier/StateNotifier-class.html
[streamprovider]: https://pub.dev/documentation/hooks_riverpod/latest/hooks_riverpod/StreamProvider-class.html
[statelesswidget]: https://api.flutter.dev/flutter/widgets/StatelessWidget-class.html
[statefulwidget]: https://api.flutter.dev/flutter/widgets/StatefulWidget-class.html
[state]: https://api.flutter.dev/flutter/widgets/State-class.html
